/*
 * Copyright 2011 Katsunori Koyanagi
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package sweezy.swf.tools.abcdump
{
	import flash.utils.Dictionary;
	import sweezy.core.utils.StringUtils;
	import sweezy.swf.abc.structure.ConstantPoolInfo;
	import sweezy.swf.abc.structure.Multiname;
	import sweezy.swf.abc.structure.MultinameA;
	import sweezy.swf.abc.structure.MultinameInfo;
	import sweezy.swf.abc.structure.MultinameL;
	import sweezy.swf.abc.structure.MultinameLA;
	import sweezy.swf.abc.structure.NamespaceInfo;
	import sweezy.swf.abc.structure.NamespaceSetInfo;
	import sweezy.swf.abc.structure.QNameA;
	import sweezy.swf.abc.structure.QNameInfo;
	import sweezy.swf.abc.structure.RTQName;
	import sweezy.swf.abc.structure.RTQNameA;
	import sweezy.swf.abc.structure.TraitInfo;
	import sweezy.swf.abc.structure.TypeName;
	import sweezy.swf.constant.MultinameKind;
	import sweezy.swf.constant.OptionDetailKind;
	import sweezy.swf.constant.TraitType;

	internal class Utils
	{

		public static const INTERNAL:Namespace = new Namespace("internal");

		public static const PRIVATE:Namespace = new Namespace("private");

		public static const PROTECTED:Namespace = new Namespace("protected");

		public static const PUBLIC:Namespace = new Namespace("public");

		private static var _functionTraitTypes:Dictionary;

		private static var _variableTraitTypes:Dictionary;

		public static function getConstantValue(constantPool:ConstantPoolInfo, index:uint, kind:uint):*
		{
			if (index === 0)
			{
				return undefined;
			}

			switch (kind)
			{
				case OptionDetailKind.UNDEFINED:
					return undefined;
				case OptionDetailKind.UTF8:
					return constantPool.stringConstants[index];
				case OptionDetailKind.INT:
					return constantPool.integerConstants[index];
				case OptionDetailKind.UINT:
					return constantPool.unsignedIntegerConstants[index];
				case OptionDetailKind.DOUBLE:
					return constantPool.doubleConstants[index];
				case OptionDetailKind.TRUE:
					return true;
				case OptionDetailKind.FALSE:
					return true;
				case OptionDetailKind.NULL:
					return null;
				case OptionDetailKind.NAMESPACE:
				case OptionDetailKind.PACKAGE_NAMESPACE:
				case OptionDetailKind.PACKAGE_INTERNAL_NS:
				case OptionDetailKind.PROTECTED_NAMESPACE:
				case OptionDetailKind.EXPLICIT_NAMESPACE:
				case OptionDetailKind.STATIC_PROTECTED_NS:
				case OptionDetailKind.PRIVATE_NS:
					return new Namespace(namespaceToString(constantPool, index));
			}

			return undefined;
		}

		public static function getConstantValueToString(constantPool:ConstantPoolInfo, index:uint, kind:uint):String
		{
			var value:* = getConstantValue(constantPool, index, kind);
			if (value is String)
			{
				return StringUtils.escapeESString(value);
			}
			else if (value is Namespace)
			{
				return "/*namespace*/" + String(value);
			}

			return String(value);
		}

		public static function isMethod(trait:TraitInfo):Boolean
		{
			return trait === null ? false : trait.traitType in _functionTraitTypes;
		}

		public static function isVariable(trait:TraitInfo):Boolean
		{
			return trait === null ? false : trait.traitType in _variableTraitTypes;
		}

		public static function multinameToString(constantPool:ConstantPoolInfo, index:uint):String
		{
			var mn:MultinameInfo = constantPool.multinameInfos[index];
			if (mn === null)
			{
				return "";
			}

			var nsset:uint;
			var ns:uint;
			var name:uint;
			var result:String;

			switch (mn.kind)
			{
				case MultinameKind.MULTINAME:
					nsset = (mn as Multiname).namespaceSet;
					name = (mn as Multiname).name;
					break;
				case MultinameKind.MULTINAME_A:
					nsset = (mn as MultinameA).namespaceSet;
					name = (mn as MultinameA).name;
					break;
				case MultinameKind.MULTINAME_L:
					nsset = (mn as MultinameL).namespaceSet;
					break;
				case MultinameKind.MULTINAME_L_A:
					nsset = (mn as MultinameLA).namespaceSet;
					break;
				case MultinameKind.QNAME:
					ns = (mn as QNameInfo).ns;
					name = (mn as QNameInfo).name;
					break;
				case MultinameKind.QNAME_A:
					ns = (mn as QNameA).ns;
					name = (mn as QNameA).name;
					break;
				case MultinameKind.RT_QNAME:
					name = (mn as RTQName).name;
					break;
				case MultinameKind.RT_QNAME_A:
					name = (mn as RTQNameA).name;
					break;
			}

			switch (mn.kind)
			{
				case MultinameKind.MULTINAME:
				case MultinameKind.MULTINAME_A:
					result = namespaceSetToString(constantPool, nsset);
					if (result.length > 0)
					{
						result += "::";
					}
					if (name === 0)
					{
						result += "*";
					}
					else
					{
						result += constantPool.stringConstants[name];
					}
					break;
				case MultinameKind.MULTINAME_L:
				case MultinameKind.MULTINAME_L_A:
					result = namespaceSetToString(constantPool, nsset);
					break;
				case MultinameKind.NAME_L:
				case MultinameKind.NAME_L_A:
					result = "";
					break;
				case MultinameKind.QNAME:
				case MultinameKind.QNAME_A:
					result = namespaceToString(constantPool, ns, true);
					if (result.length > 0)
					{
						result += "::";
					}
					if (name === 0)
					{
						result += "*";
					}
					else
					{
						result += constantPool.stringConstants[name];
					}
					break;
				case MultinameKind.RT_QNAME:
				case MultinameKind.RT_QNAME_A:
					if (name === 0)
					{
						result = "*";
					}
					else
					{
						result = constantPool.stringConstants[name];
					}
					break;
				case MultinameKind.RT_QNAME_L:
				case MultinameKind.RT_QNAME_L_A:
					result = "";
					break;
				case MultinameKind.TYPE_NAME:
					result = typenameToString(constantPool, mn as TypeName);
			}

			return result;
		}

		public static function namespaceSetToString(constantPool:ConstantPoolInfo, index:uint):String
		{
			var nsset:NamespaceSetInfo = constantPool.namespaceSetInfos[index];
			if (nsset === null)
			{
				return "";
			}

			var result:Array = [];
			for each (var i:uint in nsset.namespaceInfos)
			{
				result[result.length] = namespaceToString(constantPool, i);
			}

			return result.join(",");
		}

		public static function namespaceToString(constantPool:ConstantPoolInfo, index:uint, multiname:Boolean = false):String
		{
			var ns:NamespaceInfo = constantPool.namespaceInfos[index];
			if (ns === null)
			{
				return "";
			}
			if (ns.name === 0)
			{
				return multiname ? "*" : "";
			}

			return constantPool.stringConstants[ns.name];
		}

		private static function typenameToString(constantPool:ConstantPoolInfo, typeName:TypeName):String
		{
			var result:String = multinameToString(constantPool, typeName.typeName);
			var delim:String = "";
			result += ".<";

			for each (var type:uint in typeName.types)
			{
				result += delim;
				result += type === 0 ? "*" : multinameToString(constantPool, type);
				delim = ", ";
			}
			result += ">";

			return result;
		}

		{
			_functionTraitTypes = new Dictionary();
			_functionTraitTypes[TraitType.GETTER] = null;
			_functionTraitTypes[TraitType.SETTER] = null;
			_functionTraitTypes[TraitType.METHOD] = null;

			_variableTraitTypes = new Dictionary();
			_variableTraitTypes[TraitType.SLOT] = null;
			_variableTraitTypes[TraitType.CONST] = null;
		}
	}
}
